package org.budo.dubbo.protocol.http.sign;

import java.net.InetSocketAddress;

import org.budo.dubbo.protocol.http.Consts;
import org.budo.dubbo.protocol.http.exception.remoting.ProviderSideRuntimeException;
import org.budo.dubbo.protocol.http.exception.remoting.SignCheckException;
import org.budo.dubbo.protocol.http.invocation.BudoDubboRpcInvocation;
import org.budo.dubbo.protocol.http.util.BudoBeanUtil;
import org.budo.dubbo.protocol.http.util.BudoReflectionUtil;
import org.budo.dubbo.rpc.util.InvocationUtil;
import org.budo.support.java.net.util.JavaNetUtil;

import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.RpcResult;

import lombok.extern.slf4j.Slf4j;

/**
 * provider-side-sign
 * 
 * @author lmw
 * @see org.budo.dubbo.protocol.http.sign.SignCheck
 */
@Slf4j
public class ProviderSideSignFilter implements Filter {
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        SignCheck signCheckAnnotation = BudoReflectionUtil.getMethodAnnotationCached_2(invoker.getInterface(), //
                invocation.getMethodName(), //
                invocation.getParameterTypes(), //
                SignCheck.class);

        if (null == signCheckAnnotation || !signCheckAnnotation.value()) {
            return invoker.invoke(invocation);
        }

        // TODO 可以考虑支持多个 service 逐个尝试
        // SpringUtil.getBeanListByTypeCached(type)
        // SpringUtil.getBeanCached(ApplicationContext, String)
        SignCheckService signCheckService = (SignCheckService) BudoBeanUtil.getBeanCached(SignCheckService.class, null);
        if (null == signCheckService) {
            String message = "#44 signCheckService required, invocation=" + invocation + ", this=" + this + ", applicationContext=" + BudoBeanUtil.getApplicationContext();
            throw new ProviderSideRuntimeException(message);
        }

        // Sign 校验
        Boolean signCheck = signCheckService.signCheck((BudoDubboRpcInvocation) invocation);
        if (signCheck) {
            return invoker.invoke(invocation); // 正常执行请求
        }

        // Sign 校验失败
        InetSocketAddress remoteAddress = RpcContext.getContext().getRemoteAddress();
        String consumerSideSign = invocation.getAttachment(Consts.HeaderKey.Sign);

        String message = "#51 sign check error" //
                + ", this.ipAddresses=" + JavaNetUtil.getIpAddresses() //
                + ", remoteAddress=" + remoteAddress //
                + ", consumerSideSign=" + consumerSideSign //
                + ", providerInvocation=" + InvocationUtil.toString(invocation) //
                + ", signCheckService=" + signCheckService;

        log.error(message);

        Throwable exception = new SignCheckException(message, SignCheckException.Fail.SIGN_ERROR);
        return new RpcResult(exception); // 客户端异常
    }
}